`timescale 1ns / 1ps
/*
 * Copyright (c) 2020-2021, SERI Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-10-29     Lyons        first version
 */

module pa_soc_ram (
    clk_i,                      // system clock
    rst_n_i,                    // system reset

    addr_i,                     // address

    data_we_i,                  // write enable
    data_size_i,                // write size
    data_i,                     // write data

    data_o                      // read data
    );

`include "../pa_chip_param.v"

input                           clk_i;
input                           rst_n_i;

input  [`ADDR_BUS_WIDTH-1:0]    addr_i;

input                           data_we_i;
input  [2:0]                    data_size_i;
input  [`DATA_BUS_WIDTH-1:0]    data_i;

output [`DATA_BUS_WIDTH-1:0]    data_o;

wire                            clk_i;
wire                            rst_n_i;

wire [`ADDR_BUS_WIDTH-1:0]      addr_i;

wire                            data_we_i;
wire [2:0]                      data_size_i;
wire [`DATA_BUS_WIDTH-1:0]      data_i;

wire [`DATA_BUS_WIDTH-1:0]      data_o;


wire [`ADDR_BUS_WIDTH-1:0]      addr;

assign addr[`ADDR_BUS_WIDTH-1:0] = {2'b0, addr_i[31:2]};

reg  [3:0]                      addr_mask;

wire                            size_word;
wire                            size_half;

assign size_word = data_size_i[2];
assign size_half = data_size_i[1];

always @ (*) begin
case (addr_i[1:0])
    2'b00 : addr_mask[3:0] <= {size_word, size_word, (size_word || size_half), 1'b1};
    2'b01 : addr_mask[3:0] <= {4'b0010};
    2'b10 : addr_mask[3:0] <= {size_half, 3'b100};
    2'b11 : addr_mask[3:0] <= {4'b1000};
endcase
end

`ifdef RAM_MODE_BRAM

wire [`DATA_BUS_WIDTH-1:0]      _data;

blk_mem_ram u_blk_mem_ram0 (
    .clka                       (clk_i),
    .wea                        (data_we_i & addr_mask[3]),
    .addra                      (addr),
    .dina                       (data_i[31:24]),
    .douta                      (_data[31:24])
);

blk_mem_ram u_blk_mem_ram1 (
    .clka                       (clk_i),
    .wea                        (data_we_i & addr_mask[2]),
    .addra                      (addr),
    .dina                       (data_i[23:16]),
    .douta                      (_data[23:16])
);

blk_mem_ram u_blk_mem_ram2 (
    .clka                       (clk_i),
    .wea                        (data_we_i & addr_mask[1]),
    .addra                      (addr),
    .dina                       (data_i[15:8]),
    .douta                      (_data[15:8])
);

blk_mem_ram u_blk_mem_ram3 (
    .clka                       (clk_i),
    .wea                        (data_we_i & addr_mask[0]),
    .addra                      (addr),
    .dina                       (data_i[7:0]),
    .douta                      (_data[7:0])
);

`else

reg  [`DATA_BUS_WIDTH-1:0]      _ram[0:`RAM_SIZE*1024-1];

initial begin
    for (integer i=0; i<`RAM_SIZE*1024; i=i+1) begin
        _ram[i] = `ZERO_WORD;
    end
end

always @ (posedge clk_i) begin
    if (data_we_i) begin
        _ram[addr] <= {    data_i[31:24] & {8{ addr_mask[3]}},
                           data_i[23:16] & {8{ addr_mask[2]}},
                           data_i[15:8]  & {8{ addr_mask[1]}},
                           data_i[7:0]   & {8{ addr_mask[0]}}}
                    | {_ram[addr][31:24] & {8{~addr_mask[3]}},
                       _ram[addr][23:16] & {8{~addr_mask[2]}},
                       _ram[addr][15:8]  & {8{~addr_mask[1]}},
                       _ram[addr][7:0]   & {8{~addr_mask[0]}}};
    end
end

reg  [`DATA_BUS_WIDTH-1:0]      _data;

always @ (posedge clk_i) begin
    if (~rst_n_i) begin
        _data[`DATA_BUS_WIDTH-1:0] = `ZERO_WORD;
    end
    else begin
        _data[`DATA_BUS_WIDTH-1:0] = _ram[addr];
    end
end

`endif //`ifdef RAM_MODE_BRAM

assign data_o[`DATA_BUS_WIDTH-1:0] = _data[`DATA_BUS_WIDTH-1:0];

endmodule